Repository navigation
perf(i18n, app-shell)!: the marketplace pages' strings leave the eager language packs for a lazy marketplace namespace - #30640
objectstack-fleet[bot] wants to merge 15 commits into
Conversation
…packs into a marketplace namespace Mechanical move: each pack's marketplace group, except the two install families Studio's packages page reads, now lives in namespaces/marketplace/<code>.ts. Merged back, every catalogue equals the pack it came from (no key overlaps, no value changes). Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L6bhgXDwFw71MwSG9FKYDQ
…pace before they render Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L6bhgXDwFw71MwSG9FKYDQ
…uage The call-site-keys, dead-keys and en-drift gates now walk the lazy namespace's partial packs together with each pack, refuse a key defined in both, and keep a namespace file optional for fixture roots. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L6bhgXDwFw71MwSG9FKYDQ
…est the marketplace namespace Studio's packages page draws marketplace install rows with MarketplaceRowActions and MarketplaceNotLoaded, outside the marketplace frame, so the eleven leaves those read go back into the ten packs. A source census test in app-shell holds every marketplace key an ungated file spells to the resident pack. The namespace now tracks which catalogues each i18next instance was given, so a host resource spelling one marketplace key no longer stops the rest of that language's catalogue; a catalogue the all-ten door registered is used without a fetch; retry shows the loading state again. Adds the namespace's shape tests, its lazy runtime tests, and the marketplace pages' frame test. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L6bhgXDwFw71MwSG9FKYDQ
…r keys and English Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L6bhgXDwFw71MwSG9FKYDQ
…without useTranslation Read from react-i18next's context or its global instance and subscribe to languageChanged directly, so a marketplace component rendered without a provider does not warn NO_I18NEXT_INSTANCE. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L6bhgXDwFw71MwSG9FKYDQ
…ame out of the eager closure The loader's chunk group followed its imports and took the eager locale registry and preload helper; the frame, shared by the two lazy pages, sat in the chunk they share with the eager Studio packages page. Both groups now hold only their own module, so the loader, the frame and the ten catalogues are lazy and the eager closure changes only by the i18n-locale-en chunk's reading, recorded beside its ceiling. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L6bhgXDwFw71MwSG9FKYDQ
Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L6bhgXDwFw71MwSG9FKYDQ
…he real module Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L6bhgXDwFw71MwSG9FKYDQ
…PR body Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L6bhgXDwFw71MwSG9FKYDQ
Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L6bhgXDwFw71MwSG9FKYDQ
…as fixture data Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L6bhgXDwFw71MwSG9FKYDQ
…ses no sample-data banner The strings frame swaps the page body for its load skeleton while a new language's catalogue arrives, which unmounted SampleDataLeaveBanner and raised the banner that should follow the installer only once they leave the page. The frame now says when it is holding the page back, and the banner's cleanup asks it first. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L6bhgXDwFw71MwSG9FKYDQ
|
domain:ui#1 · os-sales · session_01L6bhgWn6zhNa4LL7VB1rtL Contract-review-claim: session_01L6bhgWn6zhNa4LL7VB1rtL · head 17e15c7 · 2026-11-05T19:57Z |
|
domain:ui#1 · os-sales · session_01L6bhgWn6zhNa4LL7VB1rtL Contract reviewServed-tier: ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgmentThe narrowing against the published spec on
Findings:
Implemented-by: VERDICT: PASS |
|
domain:ui#1 · os-sales · session_01L6bhgWn6zhNa4LL7VB1rtL 维护者速读
席位核对 — ACCEPT · head 17e15c7
|
The ten packs conflicted on the marketplace group this branch moved out. Kept this branch's packs and carried main's purgeConfirmAtMost rewrite into namespaces/marketplace/CODE.ts; each language's pack merged with its catalogue now equals main's pack. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L6bhgXDwFw71MwSG9FKYDQ
… read the package page now makes Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L6bhgXDwFw71MwSG9FKYDQ
|
domain:ui#1 · os-sales · session_01L6bhgWn6zhNa4LL7VB1rtL 维护者速读
席位核对 — ACCEPT · head 54db079
|
|
domain:ui#1 · os-sales · session_01L6bhgWn6zhNa4LL7VB1rtL Contract-review-claim: session_01L6bhgWn6zhNa4LL7VB1rtL · head 54db079 · 2026-11-06T03:56Z The |
|
domain:ui#1 · os-sales · session_01L6bhgWn6zhNa4LL7VB1rtL Contract reviewServed-tier: This record is owed on ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgmentThe narrowing against the published surface on
Findings:
Implemented-by: VERDICT: PASS |
Requested by Sales · project thread
Fixes #30588
Clause-②: yes (narrowing)
Metadata-protocol: none
Skills-docs: none — no
skills/,.claude/ordocs/text names the moved keys or teaches the pack shape (the 17marketplace.hits there aremarketplace.zod.tsand marketplace prose)维护者速读
marketplace文案包;打开市场页时才下载当前语言那一份(外加英文兜底),下载完成前页面显示原有的加载骨架,下载失败显示控制台自己的「出错了 / 重试」。Studio 软件包页也会用到的那部分文案(卸载、未加载徽标、依赖缺失与协议提示)留在语言包里不动。文字本身一个字没改。i18n-locale-en已到 59,951 / 60,000 gz,只剩 49 字节,挡住了 feat(console): a marketplace app's Install confirms what it adds before it installs #30545、feat(console): an administrator's first-run checklist on Home replaces the currency and time zone dialog #30552 两个要加市场文案的 PR;上限不许抬。现在读数 57,003,余量 2,997(合入 main 之后的 head54db079b)。en、@object-ui/i18n/locales/CODE)不再含被搬走的键,属 TypeScript 层面的收窄,changeset 写明了迁移。回滚即还原本 PR。ui/packages/i18n/src/namespaces/marketplace/index.ts(加载与就绪判断)、ui/packages/app-shell/src/console/marketplace/MarketplaceNamespaceGate.tsx(页面框架)、ui/apps/console/vite.config.ts的i18n-ns-marketplace*与marketplace-namespace-gate分组(为什么两组都要includeDependenciesRecursively: false)。pnpm --dir ui exec vitest run packages/app-shell/src/console/marketplace/__tests__/MarketplacePage.namespaceGate-30588.test.tsx packages/app-shell/src/console/marketplace/__tests__/marketplaceNamespaceBoundary.test.ts packages/i18n/src/__tests__/marketplace-namespace-lazy.test.tsx,预期 14 个用例全绿(中文页先骨架后中文、从不出现英文或原始键;切换语言时不误弹示例数据横幅)。What changes
marketplacenamespace.ui/packages/i18n/src/namespaces/marketplace/CODE.ts(ten files) are partial packs: the same shape assrc/locales/CODE.ts, holding themarketplacegroup cut out of that pack, text unchanged (each language's pack merged with its catalogue is deep-equal to its pack atbdbaf80c).index.tsloads them through ten literalimport()s and exportsuseMarketplaceNamespace(),loadMarketplaceCatalogue(),isMarketplaceCatalogueLoaded(),resolveMarketplaceLanguage()and the namespace constants. New published subpath@object-ui/i18n/namespaces/marketplace.useMarketplaceNamespace()reads the i18next instance react-i18next serves, works out the two catalogues the language needs (its own,zh-CNserved byzh, plusen, the packs'fallbackLng), merges the resident ones into the instance deep and without overwriting (a host's ownresourceswin, as they do over the packs), and answersloading/ready/failedwith aretry(). It records per instance which catalogues it installed, so a host resource that spells onemarketplace.*key does not stop the rest of that language's catalogue. A catalogue the all-ten door registered (registerBuiltInLocales()) is used without a fetch. A failed fetch stays failed untilretry()(or a later mount) asks again: no loop.MarketplaceNamespaceGatewraps the bodies ofMarketplacePageandMarketplacePackagePage: the existing copy-free skeleton (MarketplaceResolving) while loading and after a language switch, the console's residentconsole.errors.somethingWentWrongwithconsole.errors.tryAgainwhen a catalogue cannot be fetched, the page once ready. The skeleton after a language switch unmounts the page body while the visitor stays on the page, so the frame also says when it is holding the page back (marketplaceFrameHolding.ts), andSampleDataLeaveBanner's unmount cleanup asks it before raising the banner that follows an installer off the package page.views/metadata-admin/PackagesPage.tsx), outside the marketplace route, rendersMarketplaceRowActions,MarketplaceNotLoadedand thedependencyMissing/protocolCheckSkippedreaders. Their keys stay resident in all ten packs:marketplace.installedAdditiveNote,marketplace.notLoaded.*,marketplace.action.uninstall|uninstalling|details,marketplace.uninstall.confirm|confirmNotLoaded|successInList|successNotLoaded,marketplace.install.dependencyMissing.*,marketplace.install.protocolCheckSkipped.*(19 leaves inen). The newmarketplaceNamespaceBoundary.test.tsholds that set to what those files read.@object-ui/i18n/localesnow hands out each language as its pack deep-merged with its namespace catalogue and refuses a key defined in both, soall-locales-key-parityand every suite walkingbuiltInLocalesstill walk the moved keys, andregisterBuiltInLocales()still makes every string resident. The package entry'senis the resident pack alone.check-i18n-call-site-keys.mjs(collectEnKeys, hencecheck-i18n-dead-keys.mjstoo) andcheck-i18n-en-drift.mjsreadnamespaces/marketplace/CODE.tsbeside each pack, refuse a key defined in both places, and keep the namespace file optional for fixture roots. No other gate rule changed.ui/apps/console/vite.config.tsgainsi18n-ns-marketplace(the loader) and teni18n-ns-marketplace-CODEgroups, plusmarketplace-namespace-gate(the frame). The loader's and the frame's groups takeincludeDependenciesRecursively: false; the comment there gives the two measured reasons (the default stolelocales/registry.tsand the preload helper into an eager loader chunk; the frame otherwise lands in the chunk the two pages share with the eager packages page).PER_CHUNK_GZIP_CEILINGSandPER_CHUNK_BASELINEare unchanged; the new reading is recorded in prose beside'i18n-locale-en': 60_000.locales/registry.tsand the pack file names are unchanged..changeset/30588-marketplace-lazy-namespace.md,@object-ui/i18nand@object-ui/app-shellpatch, BREAKING with its migration, ADR-0087not-required (runtime-interface-only ui/packages/i18n/src/locales/en.ts#TranslationKeys).Branch: the harness-assigned
claude/project-thread-9mg01c, notclaude/issue-30588-*; the card is #30588.Tests
packages/i18n/src/__tests__/marketplace-namespace.test.ts(56 cases): ten literalimport()loaders and no static catalogue import (read by the TypeScript parser); every catalogue holds only themarketplacegroup and the probe key, no pack holds the probe key, no leaf is in both a pack and its catalogue; the all-ten door's languages are exactly pack plus catalogue; the entry'senlacks the moved keys; language resolution; the loader refuses an unknown code and memoises.packages/i18n/src/__tests__/marketplace-namespace-lazy.test.tsx(5 cases, registry re-registered as packs alone):zh-CNanswers loading first, then every render of the probe is Chinese (no English, no raw key) and onlyzhandenwere fetched; a switch tojawaits again;svis served byen; a host resource formarketplace.titlewins andmarketplace.refreshstill arrives in Chinese; a failingkochunk answers failed once, makes no second request on its own, andretry()loads it.packages/app-shell/src/console/marketplace/__tests__/MarketplacePage.namespaceGate-30588.test.tsx(4 cases, a MutationObserver records every text node the page adds): the catalogue page inzh-CNand the package page indeshow the skeleton, then their language, and never the English text or a raw key; anarchunk that fails shows the Arabic resident error words, and Try again fetches it and renders Arabic; on a package page with a sample-data record from this session, a switch fromzh-CNtojaraises no banner in any language, and leaving the page raises it in Japanese. Before the frame said it was holding, that case failed: the switch raised the banner in Chinese while the installer was still on the page (expected … not to contain '"Service Desk" 中有示例数据。').packages/app-shell/src/console/marketplace/__tests__/marketplaceNamespaceBoundary.test.ts(5 cases): finds every module outsideconsole/marketplace/that imports into it, holds that both pages render under the frame, and that everymarketplace.*key spelled by a file reachable from an ungated door is a resident pack key.scripts/__tests__/check-i18n-call-site-keys.test.tsandcheck-i18n-en-drift.test.tsgain the namespace cases (a group moved into the namespace is no drift; a change inside it is; a key in both is refused).scripts/ablation-replace.mjsreplacedMarketplacePage'sMarketplaceNamespaceGatewrapper with a bare fragment (the page no longer loads the namespace): the frame test went red, 2 failed / 3, the first frame readingexpected 'marketplace.titlemarketplace.subtitle…' not to match /\bmarketplace\.[A-Za-z]/. The same onMarketplacePackagePage: 1 failed / 3. Both restored by the tool's trap: blob equals HEAD,git diff HEADempty.69ab56e1(before the Studio keys went back), it failed 2 / 5, naming the elevenMarketplaceRowActions.tsxkeys.Acceptance notes
CI=true vite build,apps/console/dist/eager-closure.json):i18n-locale-en59,951 atbdbaf80c→ 57,010 at17e15c7f(ceiling 60,000, headroom 49 → 2,990). Eager closure 2,204,342 → 2,201,442 gz, 103 → 103 eager chunks with the same names on both sides; no other chunk moved by more than 40 bytes. Noi18n-ns-marketplace*chunk and nomarketplace-namespace-gatechunk is eager. The ten catalogue chunks read 3,724 (en) to 5,048 (ru) gz each, the loader 1,315 gz, the frame 704 gz, all lazy. The prose beside the ceiling records 2,201,440, read at5986ae70; the 2 bytes are chunk-hash noise.zh-CN(shot) andde(shot); package page inzh-CN(shot) andde(shot), 1440 wide. A Playwright run recorded every text node each page added on the way: no rawmarketplace.key and no English title or subtitle in any of them, no page error. The language files (zhanden,deanden) were requested only once the marketplace page opened; the dev server serves the loader module itself at boot, since dev has no chunks (the production readings above are the lazy ones). Shots committed ataf733e30and removed from the tree by the next commit; they predate17e15c7f, which changes only when the sample-data banner is raised.zh-Hans/zh-Hant/pt-BR; after this lands it adds the matchingnamespaces/marketplace/CODE.tsfiles and loader entries (the shape test pins one loader perBUILT_IN_LANGUAGE_CODESentry, so it will say so).marketplace.install.preview.*and related keys belong in the namespace catalogues, unless Studio's packages page reads them.Verification
All on head
17e15c7funless noted. Lock readings are the lock's own VERDICT lines.Re-run on
54db079b(origin/main merged at64857f64; PR #30653's newpurgeConfirmAtMostwording carried intonamespaces/marketplace/<lang>.tsin all ten languages, one test stub for itscountLocalSampleDataread; os-dev-report 6671129796): every pack deep-merged with its catalogue deep-equals main's pack at47c457b23; console/marketplace + i18n vitest 150 files, 1980 tests passed; uiscripts/170 files, 4979 tests;check:i18n-keys,check:i18n-dead-keys,check-i18n-en-drift.mjs --base origin/mainexit 0;i18n-locale-en57,003 gz (headroom 2,997); 57/57 derived gates exit 0; full dogfood 421 files, 3513 tests passed. The readings below are the17e15c7fones.pnpm --dir ui exec vitest run packages/app-shell/src/console/marketplace packages/i18n: 150 files passed, 1960 tests passed, 13 skipped.pnpm --dir ui exec vitest run packages/app-shell/src/views/metadata-admin(Studio's packages page and the rest of that view): run with the marketplace directory atae5aac72, 780 files, 8133 passed, 1 skipped. Later commits touch only the frame, the namespace hook, the banner and tests; the boundary test holds that nothing outside the marketplace pages reaches them.pnpm --filter @object-ui/app-shell run type-check(source and tests): exit 0.pnpm --filter @object-ui/console run type-checkthenbuild:plugin: exit 0.pnpm --filter @object-ui/i18n run type-check: exit 0 (no i18n source change afterf10a7fdf).@object-ui/i18ndist, removed after):en.marketplace.action.uninstallcompiled anden.marketplace.titlegave exactly one error,TS2339: Property 'title' does not exist;git statusclean after.pnpm --dir ui run check:i18n-keys(4,210 keys),check:i18n-dead-keys(report identical to base: same 94 entries),node scripts/check-i18n-en-drift.mjs --base bdbaf80c(0 en values changed): exit 0 each.pnpm --dir ui check:eager-closure,pnpm --dir ui check:eager-locale-catalogues: exit 0;scripts/__tests__/check-eager-closure-budget.test.ts(174): exit 0.node scripts/check-adr-0087-registration.mjs --base origin/main: verifiedruntime-interface-onlyonTranslationKeys.--no-inline-configon every changed source, test and script: 0 errors; the only warnings are pre-existing ones in the two page files.node scripts/pm/dispatch-gates.mjs --commands: 57 derived families, the same list at9cf51c22and at17e15c7f. All 57 exit 0;--ranreconciles 57 run, 0 NOT-MEASURED, 0 UNRUN.check:dual-build-cjs-loadsfirst answered PREREQUISITE NOT MET (seven root dists absent) and passed once they were built;pnpm --dir ui exec vitest run scripts/passed on17e15c7f(170 files, 4979 tests, 2 skipped). Re-run on17e15c7fafter the last commits:check:type-check-debt,check:type-check-coverage,check:nul-bytes,check:test-source-alias,check:cross-package-test-inputs,check:published-files,check:undeclared-dep-imports,check:sourcemap-no-sources-content,check-empty-changeset,check-changeset-no-major,check-adr-0087-registration, all exit 0. Outside the derived list,pnpm --dir ui run check:new-line-citations: 0 new citations.pnpm exec turbo run build --filter='@objectstack/dogfood^...'(69 tasks), thenpnpm --filter @objectstack/dogfood testunder the lock on17e15c7f: exit 0, Test Files 420 passed, 3 skipped (423); Tests 3496 passed, 36 skipped (3532). No failure.🤖 Generated with Claude Code
https://claude.ai/code/session_01L6bhgXDwFw71MwSG9FKYDQ